package commons;

import gui.mainView.UIView;

import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStreamReader;
import java.util.ArrayList;

import player.Player;
import server.airpi.Server;


import commons.exceptions.PasswordException;
import commons.exceptions.ProcessException;

public class Manager extends Thread{

	private static Manager INSTANCE = null;

	private MediaList mediaList;
	private UserList userList;
	private UIView iface;
	private AutoRefresh autoRefresh;
	private Media media;
	private boolean isPlaying;
	private boolean isStopped;
	private boolean running;
	private Server server;

	private static final int nbClient=20;

	private Manager(){
		this.mediaList=new MediaList();
		this.userList=new UserList();
		this.iface = UIView.getInstance(this);
		this.autoRefresh = new AutoRefresh(this);
		this.media=null;
		this.isPlaying=false;
		this.isStopped=false;
		this.running=true;
	}

	/**
	 * Get the single instance of Manager
	 * @return INSTANCE 
	 */
	public static synchronized Manager getInstance()
	{	
		if (INSTANCE == null){ 
			INSTANCE = new Manager();
			INSTANCE.start();
		}
		return INSTANCE;
	}

	public void run(){
		server = Server.getInstance(this.nbClient);
		server.start();
		long start = System.currentTimeMillis();
		long now = 0;
		while(running){

			//Waiting for a media
			while(this.mediaList.getFirst()==null && running){
				now=System.currentTimeMillis();
				if((now-start)>600000){
					autoRefresh.start();
					start = System.currentTimeMillis();
					System.out.println("Refresh user's list");
					autoRefresh = new AutoRefresh(this);
				}
				try {
					Thread.sleep(1000);
				} catch (InterruptedException e) {
				}
			}

			if(running){
				//Play the first media
				this.playMedia(this.mediaList.getFirst());
				this.iface.update(UIView.Update_MediaPan);
			}
			
			//As long as there is a media
			while(this.mediaList.getFirst()!=null && running){
				now=System.currentTimeMillis();
				if((now-start)>600000){
					autoRefresh.start();
					start = System.currentTimeMillis();
					System.out.println("Refresh user's list");
					autoRefresh = new AutoRefresh(this);
				}
				try {
					Thread.sleep(1000);
				} catch (InterruptedException e) {
				}
			}
		}
	}

	private synchronized void playMedia(Media media){
		System.out.println("playMedia:"+media.getMediaName());
		this.media=media;
		Player.playMedia(this, this.media.getPath());
		this.isPlaying=true;
		if(this.isStopped){
			this.iface.update(UIView.Update_SouthPan);
			this.isStopped=false;
		}
		else{
			this.iface.update(UIView.Update_InfoAndSouthPan);
		}
	}

	public synchronized void play() throws ProcessException{
		System.out.println("play():"+media.getMediaName());
		if(this.media!=null){
			if(!this.isPlaying && !this.isStopped){
				Player.action(Player.OmxPlayerCommands.PAUSE_RESUME.getCommand());
				this.isPlaying=true;
				this.iface.update(UIView.Update_SouthPan);
			}
			else if(isStopped){
				this.playMedia(this.media);
			}
			else
			{
				throw new ProcessException("Already playing");
			}
		}
		else{
			throw new ProcessException("No media");
		}
	}

	public synchronized void pause() throws ProcessException{
		System.out.println("pause():"+media.getMediaName());
		if(isPlaying()){
			Player.action(Player.OmxPlayerCommands.PAUSE_RESUME.getCommand());
			this.isPlaying=false;
			this.iface.update(UIView.Update_SouthPan);
		}
		else{
			throw new ProcessException("Not playing");
		}
	}

	public synchronized void action(String action) {
		System.out.println("action("+action+")");
		Player.action(action);
	}

	public synchronized void end() throws ProcessException{
		System.out.println("end()");
		if(mediaList.getNext()==null){
			mediaList.delMedia(getFirst());
			this.media=null;
			this.isPlaying=false;
			this.iface.update(UIView.Update_InfoMediaAndSouthPan);
		}
		else{
			this.next();
		}
	}
	
	public synchronized void next() throws ProcessException{
		System.out.println("next()");
		if(mediaList.getNext()==null){
			throw new ProcessException("No media");
		}
		else{
			if(!this.isStopped){
				Player.killCheckPlaying();
				Player.action(Player.OmxPlayerCommands.EXIT.getCommand());
			}
			this.isStopped=false;
			mediaList.delMedia(getFirst());
			this.playMedia(getFirst());
			this.iface.update(UIView.Update_MediaPan);
		}
	}

	public synchronized void stopMedia(){
		System.out.println("stopMedia:"+media.getMediaName());
		if(this.media!=null && !this.isStopped){
			Player.killCheckPlaying();
			Player.action(Player.OmxPlayerCommands.EXIT.getCommand());
			this.isStopped=true;
			this.isPlaying=false;
			this.iface.update(UIView.Update_SouthPan);
		}
	}

	public synchronized void addMedia(Media media) {
		System.out.println("addMedia: "+media.getMediaName());
		if(this.media!=null){
			mediaList.addMedia(media);
			media.getUser().updateTimeStamp();
			this.iface.update(UIView.Update_MediaPan);
		}
		else{
			mediaList.addMedia(media);
			media.getUser().updateTimeStamp();
		}
	}

	public synchronized void delMedia(Media media) throws ProcessException{
		System.out.println("DelMedia: "+media.getMediaName());
		if(media.equals(this.media)){
			if(this.mediaList.getNext()==null){
				if(!this.isStopped){
					Player.killCheckPlaying();
					Player.action(Player.OmxPlayerCommands.EXIT.getCommand());
				}
				mediaList.delMedia(getFirst());
				this.isPlaying=false;
				this.media=null;
				this.iface.update(UIView.Update_InfoMediaAndSouthPan);
			}
			else
			{
				if(!this.isStopped){
					Player.killCheckPlaying();
					Player.action(Player.OmxPlayerCommands.EXIT.getCommand());
				}
				mediaList.delMedia(getFirst());
				this.playMedia(this.mediaList.getFirst());
				this.isStopped=false;
				this.iface.update(UIView.Update_InfoAndMediaPan);
			}
		}
		else{
			mediaList.delMedia(media);
			this.iface.update(UIView.Update_MediaPan);
		}

		System.out.println("Media deleted");
	}

	public synchronized void addUser(User user) throws ProcessException {
		try {
			userList.addUser(user);
			System.out.println("User added");
			this.iface.update(UIView.Update_UserPan);
		} catch (Exception e) {
			throw new ProcessException(e.getMessage());
		}
	}

	public synchronized void delUser(User user){
		userList.delUser(user);
		this.iface.update(UIView.Update_UserPan);
	}

	public synchronized MediaList getMediaList(){
		return this.mediaList;
	}

	public synchronized ArrayList<User> getAdminArrayList(){
		return userList.getAdminArrayList();
	}

	public synchronized ArrayList<User> getGuestArrayList(){
		return userList.getGuestArrayList();
	}

	public synchronized UserList getUserList(){
		return this.userList;
	}

	public synchronized Media getFirst() throws ProcessException{
		if(this.mediaList.getFirst()==null){
			return null;
		}
		else{
			return this.mediaList.getFirst();
		}
	}

	public synchronized boolean isPlaying(){
		return this.isPlaying;
	}

	/**
	 * Tests if a media is currently being played
	 * @return True if a media is being played, false otherwise
	 */
	public synchronized boolean isStopped(){
		return this.isStopped;
	}

	public synchronized Media getMediaPlaying(){
		return this.media;
	}

	public String getIpAddress(){
		ProcessBuilder pb = new ProcessBuilder("/bin/sh", "-c", "hostname -I");
		pb.redirectErrorStream(true);
		java.lang.Process proc;
		String ip="";
		try {
			proc = pb.start();
			BufferedReader reader = new BufferedReader(new InputStreamReader(proc.getInputStream()));
			ip=reader.readLine();
			if(!ip.equals("")){
				ip=ip.substring(0,ip.length()-1);
				return ip;
			}
			else return "";
		} catch (IOException e) {
			return ip;
		}
	}

	public synchronized void changeAdminPassword(String currentPasswd,String newPasswd) throws PasswordException{
		User.setAdminPassword(currentPasswd,newPasswd);
	}

	public synchronized void changeGuestPassword(String currentPasswd,String newPasswd) throws PasswordException{
		User.setGuestPassword(currentPasswd,newPasswd);
		this.iface.update(UIView.Update_GuestPan);
	}

	public void kill(){
		this.server.stopThread();
		this.iface.kill();
		this.iface=null;
		this.autoRefresh.kill();
		this.autoRefresh=null;
		this.running=false;
	}

	public void updateIface(Integer cmd){
		this.iface.update(cmd);
	}
}
